# orm db init
import datetime
import sqlalchemy
from sqlalchemy import create_engine, Column, Integer, String, ForeignKey, select
from sqlalchemy.orm import declarative_base, mapped_column, Mapped, sessionmaker, relationship, aliased, outerjoin
from typing_extensions import Annotated
from typing import List

engine = create_engine('mysql://root:ZHabc123.@page.chenzao.fun:13306/testdb', echo=True)

Base = declarative_base()

int_pk = Annotated[int, mapped_column(Integer, primary_key=True)]
name_str_require_union = Annotated[str, mapped_column(String(128), nullable=False, unique=True)]
timestamp_not_null = Annotated[datetime.datetime, mapped_column(nullable=False)]


class Department(Base):
    __tablename__ = 'department'
    id: Mapped[int_pk]
    name: Mapped[name_str_require_union]
    
    # 使用 back_populates 参数，可以解决双向关联的麻烦, 可以在 Department 类中，通过 members 这个属性，来访问 Member 类
    # 属于显性的双向关联，这个比较常用，因为可以很清楚的知道，在 Department 类中，有一个 members 属性，是一个集合类型的
    # 但是在实际开发中，这种一对多的关联关系里，通常不会去写这种方便的关联关系，因为这样会降低数据库的性能。
    members: Mapped[List['Member']] = relationship(back_populates='department')
    
    
    def __repr__(self):
        # 直接打印对象的时候会调用这个方法，返回一个字符串，表示部门的ID和名称
        return f'Department(id={self.id}, name={self.name})'


class Member(Base):
    __tablename__ = 'member'
    id: Mapped[int_pk]
    department_id: Mapped[int] = mapped_column(ForeignKey("department.id"))
    name: Mapped[name_str_require_union]
    birthday: Mapped[timestamp_not_null]
    
    # 这不是跟数据库中的字段进行关联，而是跟 Department 类进行关联，放在内存中，方便后面使用。这是一个关系字段
    # backref 参数表示，在 Department 类中，有一个成员的字段，是 Member 类的实例，是一个集合类型的，相当于双向绑定
    # backref 这种方式的双向关联，虽然好用，但是单看 Department 类，无法知道有 members 这个属性，所以不太经常用
    # department: Mapped[Department] = relationship(lazy=False, backref="members")
    # 使用 back_populates 参数，可以解决双向关联的麻烦, 可以在 Department 类中，通过 members 这个属性，来访问 Member 类
    # 属于显性的双向关联，这个比较常用，因为可以很清楚的知道，在 Department 类中，有一个 members 属性，是一个集合类型的
    # 一对一是可以的
    department: Mapped[Department] = relationship(lazy=False, back_populates='members')
    
    def __repr__(self):
        return f'Member(id={self.id}, department_id={self.department_id}, name={self.name})'
    
Base.metadata.create_all(engine)
Session = sessionmaker(bind=engine)